<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>wave使用canvas实现</title>
    <style>
        html,
        body {
            padding: 0;
            margin: 0;
            width: 100%;
            height: 100%;
        }

        canvas {
            width: 100%;
            height: 100%;
            background: #232323;
        }
    </style>
</head>

<body>
    <!-- 使用canvas进行水波纹的绘制 -->
    <script>
        function valueMapping(x, inMin, inMax, outMin, outMax) {
            return (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
        }

        const canvas = {
            init() {
                this.ele = document.createElement('canvas')
                document.body.appendChild(this.ele)
                this.resize()
                window.addEventListener('resize', () => this.resize(), false)
                this.ctx = this.ele.getContext('2d')
                return this.ctx
            },
            onResize(callback) {
                this.resizeCallback = callback
            },
            resize() {
                this.width = this.ele.width = this.ele.offsetWidth
                this.height = this.ele.height = this.ele.offsetHeight
                this.resizeCallback && this.resizeCallback()
            },
            run(callback) {
                requestAnimationFrame(() => {
                    this.run(callback)
                })
                callback(this.ctx)
            }
        }

        const ctx = canvas.init()

        class Wave {
            constructor(cavans, options) {
                this.canvas = canvas
                this.options = options
                this.xMove = this.options.offset
                this.xSpeed = this.options.xSpeed
                this.resize()
            }
            resize() {
                this.width = canvas.width
                this.height = canvas.height
                this.amplitude = this.canvas.height * this.options.amplitude
            }
            draw(ctx) {
                ctx.beginPath()
                this.xMove += this.xSpeed
                ctx.moveTo(0, this.height / 2) //moveTo方法先保存的一个坐标
                var grad = ctx.createLinearGradient(0, 0, this.width, 0);
                grad.addColorStop(0, this.options.start);
                grad.addColorStop(1, this.options.stop);
                ctx.strokeStyle = grad
                ctx.lineWidth = this.options.lineWidth
                for (let x = 0; x < this.width; x++) {
                    const scale = (Math.sin(x / this.width * Math.PI * 2 - Math.PI * 0.5) + 1) * 0.5
                    const y = Math.sin(x * 0.02 + this.xMove) * this.amplitude * scale + this.height / 2
                    ctx.lineTo(x, y) // lineTo是从moveTo坐标移动到lineTo坐标
                }
                ctx.stroke() //绘制出实际的路径的方法
                ctx.closePath()
            }
        }

        const gradients = [
            ['#6e45e2', '#88d3ce'],
            ['#de6262', '#ffb88c'],
            ['#64b3f4', '#c2e59c'],
            ['#0fd850', '#f9f047'],
            ['#007adf', '#00ecbc'],
            ['#B6CEE8', '#F578DC'],
            ['#9be15d', '#00e3ae']
        ]

        let waves = []
        
        // 初始化操作
        const init = () => {
            waves = []
            for (let i = 0; i < 5; i++) {
                const [start, stop] = gradients[Math.floor(Math.random() * gradients.length)]
                waves.push(new Wave(canvas, {
                    start,
                    stop,
                    lineWidth: 1,
                    xSpeed: valueMapping(Math.random(), 0, 1, -0.05, -0.08),
                    amplitude: valueMapping(Math.random(), 0, 1, 0.05, 0.5),
                    offset: Math.random() * 100
                }))
            }
        }
        
        // 单击document触发初始化
        document.addEventListener('click', () => {
            init()
        })

        init()

        canvas.run(ctx => {
            ctx.clearRect(0, 0, canvas.width, canvas.height)
            waves.forEach(wave => {
                wave.draw(ctx)
            })
        })
        
        // 画布大小变化触发初始化
        canvas.onResize(() => {
            init()
        })
    </script>
</body>

</html>